import tokenize as tk
from hiast import *


class Lexer(object):
    def __init__(self, fname):
        self.f = open(fname)
        self.lex = tk.generate_tokens(self.f.readline)
        self.cur = next(self.lex)

    def get_next(self):
        if self.cur is None:
            self.cur = next(self.lex)

        # skip NEWLINE and NL
        if self.cur.type == 4 or self.cur.type == 62:
            self.cur = next(self.lex)
            print(self.cur)

        return self.cur

    def report_error(self, msg):
        print(msg)
        exit()

    def match(self, ttype):
        if self.cur is None:
            self.report_error("")

        if self.cur.type != ttype:
            self.report_error("expected %s, but got %s" % (ttype, self.cur.type))

        self.cur = next(self.lex)

        if self.cur.type == 62 or self.cur.type == 4:
            self.cur = next(self.lex)
        print(self.cur)


lexer = Lexer("fib.txt")

# stmts = stmt (stmt) *
# assign_stmt ->  ID '=' expression;
# print_stmt ->   'print' '(' expression ')' ;

def stmts():
    lst = ListNode()
    a = stmt()
    lst.append(a)
    t = lexer.get_next()

    while (t.type == 1):
        a = stmt()
        lst.append(a)
        t = lexer.get_next()

    return lst

# stmt = assign_stmt |
#        print_stmt
def stmt():
    t = lexer.get_next()
    if (t.type == 1 and t.string == "print"):
        return print_stmt()
    elif (t.type == 1 and t.string == "if"):
        return if_stmt()
    elif (t.type == 1 and t.string == "while"):
        return while_stmt()
    else:
        return assign_stmt()

def print_stmt():
    lexer.match(1)
    lexer.match(54)   # '('
    t = expression()
    lexer.match(54)
    lexer.match(54)
    return PrintNode(t)

def assign_stmt():
    t = lexer.get_next()
    name = t.string
    lexer.match(1)
    lexer.match(54)  # '='
    t = expression()
    lexer.match(54)  # ';'
    return AssignNode(name, t)

def if_stmt():
    t = lexer.get_next()
    lexer.match(1)
    lexer.match(54)   # '('
    condition = expression()
    lexer.match(54)
    lexer.match(54)   # '{'
    then_block = stmts()
    lexer.match(54)
    t = lexer.get_next()
    else_block = []
    if (t.type == 1 and t.string == "else"):
        lexer.match(t.type)
        lexer.match(54)   # '{'
        else_block = stmts()
        lexer.match(54)
    return IfNode(condition, then_block, else_block)

def while_stmt():
    t = lexer.get_next()
    lexer.match(1)
    lexer.match(54)   # '('
    condition = expression()
    lexer.match(54)
    lexer.match(54)   # '{'
    then_block = stmts()
    lexer.match(54)

    return WhileNode(condition, then_block)

def expression():
    a = algrithm()
    t = lexer.get_next()

    while (t.type == 54 and (t.string == "<" or t.string == ">"
            or t.string == "<=" or t.string == ">=" or t.string == "==")):
        lexer.match(t.type)
        b = algrithm()
        a = BinaryOpNode(a, b, t.string)

        t = lexer.get_next()

    return a
    
# expression = term ( ('+'|'-') term ）*
# term = factor (('*' | '/') factor) *
# factor = NUMBER | '(' expr ')'

# expression = term (+ term)*
def algrithm():
    a = term()
    t = lexer.get_next()

    while (t.type == 54 and (t.string == "+" or t.string == "-")):
        lexer.match(t.type)
        b = term()
        a = BinaryOpNode(a, b, t.string)

        t = lexer.get_next()

    return a

# term = factor * factor
def term():
    a = factor()
    t = lexer.get_next()

    while (t.type == 54 and (t.string == "*" or t.string == "/")):
        lexer.match(t.type)
        b = factor()
        a = BinaryOpNode(a, b, t.string)

        t = lexer.get_next()

    return a

# factor = - factor
# factor = NUMBER | '(' expression ')'
def factor():
    v = lexer.get_next()
    if v.type == 2:
        lexer.match(v.type)
        return ConstValueNode(int(v.string))

    elif v.type == 1:
        n = NameNode(v.string)
        lexer.match(v.type)
        return n

    elif v.type == 54 and v.string == "(":
        lexer.match(v.type)
        t = expression()
        lexer.match(54)  # ")"
        return t


tree = stmts()
Evaluator().visit(tree)
